home *** CD-ROM | disk | FTP | other *** search
- /* ====================================================================== */
-
- /* DiskSalv
-
- AmigaDOS Disk Salvage Program
- by
- Dave Haynie
-
- (c) April 1986
-
- May be copied magnetically or electronically without the permission
- of the author, providing no charge is requested for the program. The
- only fees permitted are a moderate copying charge, in the case of
- magnetic copying, or any normal BBS charges, in the case of an
- electronic download. Any other uses may be negotiated by contacting
- the author. This program is copyrighted; portions of this program
- source may be used as is in other programs with no necessary
- acknowledgement, though acknowledgements are always appreciated.
-
- If you are very pleased with this program or what you learn from
- the source code, please consider sending no more than $10.00 to
- the author. In addition, any bug reports would be greatly
- appreciated. All correspondence may be sent to:
-
- Dave Haynie
- 645 Allen Avenue
- Gibbstown, NJ 08027
-
- Usenet: {allegra,caip,seismo}!cbmvax!daveh
- Compuserve: 76703,2047
- QuantumLink: Animal128
-
- These addresses are current as of this writing, most are likely to
- remain so for awhile.
-
- ANYWAY, this program is designed to let the user salvage all or parts
- of a damaged AmigaDOS disk. It is used similarly to DISKCOPY or
- COPY, with the format:
-
- DISKSALV dfN: [TO] dfM:
-
- Where the source disk is the corrupted disk, the target disk is
- a formatted AmigaDOS disk, usually empty. This command
- currently takes two drives; i.e. it won't salvage df0: to df0:.
-
- This program salvages the bad disk by by-passing the normal
- disk root directory, which may be corrupted (and as well is
- probably the most likely cause of a disk being rejected by
- AmigaDOS). It performs a scan to collect all directories and
- their associated files, in pointer form. Then the directories
- are created one-by-one through normal DOS calls, then the files
- are read and placed in the directories. There is no necessary
- correlation between the physical location of files on the bad
- disk and the physical location of the corresponding files on
- the newly created disk. Once all linkable files are restored,
- a directory of existing but unused data blocks is created, in an
- effort to preserve anything left, which might be manually
- re-created. This program will also copy a good disk, though
- inefficiently as compared to a straight DiskCopy. The program
- will prompt for a new disk if it is having trouble writing to
- the given recovery disk at any time.
- */
-
- #include <exec/types.h>
- #include <exec/exec.h>
- #include <devices/trackdisk.h>
- #include <libraries/dos.h>
- #include <libraries/dosextens.h>
- #include <ctype.h>
- #include <stdio.h>
-
- /* ====================================================================== */
-
- /* Global definitions. */
-
- char prog_version[] = "1.0";
-
- #define BLOCKSIZE TD_SECTOR
- #define WORDSIZE sizeof(ULONG)
- #define DISK_SIZE (NUMHEADS*NUMCYLS*NUMSECS)
- #define SIZE (BLOCKSIZE / WORDSIZE)
- #define LIST_SIZE (SIZE - (50+6))
- #define READOK 0
- #define BSTR_MAX 31
- #define PATH_MAX 255
-
- #define FT_SHORT_FILE 2 /* Short file type */
- #define FT_DATA_BLOCK 8 /* Data block type */
- #define ST_FILE 0xfffffffd /* File subtype */
- #define ST_USER_DIR 2 /* User directory subtype */
-
- struct IOExtTD *diskreq = NULL; /* Pointer for extended disk commands */
- struct MsgPort *diskport = NULL; /* Port for trackdisk replies */
-
- LONG diskChangeCount = 0; /* Checks for changed disk */
- int device = 1; /* Default disk device number */
-
- /* ====================================================================== */
-
- /* Buffer access types. Each of these types is a template of BLOCKSIZE
- used to access a TRACKDISK data block's individual elements. There are
- several types of these data blocks, "root", the disk's root directory,
- "directory", a user defined directory block, "file", a file header
- block, "extens", a file extension block, and "data", a data block
- (the real meat of what's out there on disk. I also define a "generic"
- block, which contains most of the things that you'd like to look at
- in a block who's fundamental and secondary types haven't yet been
- determined. */
-
- /* This block type contains the elements that are found in most blocks
- and usually required to identify the type of the block. Called
- as .Gen in the block union. */
-
- struct generic_block {
- ULONG blk_type, blk_key, blk_seq, blk_size, blk_link, blk_checksum;
- UBYTE blk_data[BLOCKSIZE - 9*WORDSIZE];
- ULONG blk_parent, blk_extension, blk_subtype;
- };
-
- /* This is the block that's used to store the actual data on the disk. Its
- accesses as .Dat in the block union. */
-
- struct data_block {
- ULONG blk_type, blk_key, blk_seq, blk_size, blk_next, blk_checksum;
- UBYTE blk_data[BLOCKSIZE-6*WORDSIZE];
- };
-
- /* This block is used as the root directory for the entire disk. Its
- accessed as .Rot in the block union. */
-
- struct root_block {
- ULONG blk_type, blk_key, blk_seq, blk_size, blk_runused, blk_checksum;
- ULONG blk_hashtable[LIST_SIZE];
- ULONG blk_bmflag, blk_bitmap[49-23];
- ULONG blk_days, blk_mins, blk_ticks;
- UBYTE blk_name[(20-7)*WORDSIZE];
- ULONG blk_createdays, blk_createmins, blk_createticks;
- ULONG blk_nexthash, blk_parent, blk_extension, blk_subtype;
- };
-
- /* This block is a user directory block. It is accessed as .Dir in the
- block union. */
-
- struct directory_block {
- ULONG blk_type, blk_key, blk_seq, blk_dunused1[2], blk_checksum;
- ULONG blk_hashtable[LIST_SIZE];
- ULONG blk_spare, blk_protect, blk_dunused2;
- UBYTE blk_comment[(47-23)*WORDSIZE];
- ULONG blk_days, blk_mins, blk_ticks;
- UBYTE blk_name[(20-4)*WORDSIZE];
- ULONG blk_nexthash, blk_parent, blk_extension, blk_subtype;
- };
-
- /* This block stores a file header, which contains the information used to
- link together the individual elements of a file. Its accessed as .Fil
- in the block union. */
-
- struct file_block {
- ULONG blk_type, blk_key, blk_seq, blk_size, blk_firstdata, blk_checksum;
- ULONG blk_datalist[LIST_SIZE];
- ULONG blk_spare, blk_protect, blk_bytesize;
- UBYTE blk_comment[(47-23)*WORDSIZE];
- ULONG blk_days, blk_mins, blk_ticks;
- UBYTE blk_name[(20-4)*WORDSIZE];
- ULONG blk_nexthash, blk_parent, blk_extension, blk_subtype;
- };
-
- /* This block stores a file extension block, which is only used if the file
- is so long that the main file header doesn't fil all of the data links it
- will need. The file header can currently store data for (BLOCKSIZE-
- WORDSIZE*(50+6)) = 72 data blocks, or (72*(BLOCKSIZE-6*WORDSIZE)) = 35,136
- bytes. It is accessed as .Ext in the block union */
-
- struct extens_block {
- ULONG blk_type, blk_key, blk_seq, blk_size, blk_firstdata, blk_checksum;
- ULONG blk_datalist[LIST_SIZE];
- UBYTE blk_eunused[(50-4)*WORDSIZE];
- ULONG blk_nexthash, blk_parent, blk_extension, blk_subtype;
- };
-
- /* And this packs them all together */
-
- union block {
- struct generic_block Gen;
- struct data_block Dat;
- struct root_block Rot;
- struct directory_block Dir;
- struct file_block Fil;
- struct extens_block Ext;
- };
-
- union block *buf = NULL; /* Global disk buffer pointer */
- struct data_block *data = NULL; /* Global disk data block */
- struct extens_block *extn = NULL; /* Global extension block */
-
- /* ====================================================================== */
-
- /* This section contains structures used to store located directory names
- and pointers to the files to be restored below them. */
-
- struct d_tree {
- struct d_tree *d_next; /* Next Directory on same level */
- struct d_tree *d_parent; /* Directory on upper level */
- struct d_tree *d_child; /* First Directory on lower level */
- char d_name[BSTR_MAX+1]; /* Directory Name */
- int d_flags; /* Directory build flags */
- ULONG d_point; /* Directory Disk Pointer */
- struct f_list *d_list; /* Files in directory */
- };
-
- struct f_list {
- struct f_list *f_next; /* Next file in directory */
- ULONG f_file; /* Disk location of file header */
- };
-
- struct d_tree *root = NULL; /* Global directory root */
-
- /* ====================================================================== */
-
- /* This section defines the bit-maps. One will speed up the system, making
- it read error blocks only once. The other keeps track of used directory
- blocks. */
-
- UBYTE usdmap[DISK_SIZE/8]; /* Used directory blocks. */
- UBYTE badmap[DISK_SIZE/8]; /* Bad directory blocks. */
-
- /* This function zeros out a given bitmap. */
-
- VOID zeromap(map)
- UBYTE map[];
- {
- int i;
-
- for (i=0; i < DISK_SIZE/8; i++) map[i] = 0;
- }
-
- /* This function returns TRUE if the given block number is marked
- in the given bit-map. */
-
- BOOL checkmap(map,num)
- UBYTE map[];
- ULONG num;
- {
- return (BOOL) ((map[num>>3] & (1 << num%8)) != 0);
- }
-
- /* This function sets the given block number in the bit map. */
-
- VOID setmap(map,num)
- UBYTE map[];
- ULONG num;
- {
- map[num>>3] |= (1 << num%8);
- }
-
- /* ====================================================================== */
-
- /* This section contains basic functions. */
-
- /* This function converts a BSTR to a normal "C" string, returning that
- C string as its value. The passed C string pointer is assumed to
- contains enough space to store the returned C string. */
-
- char *bstr_to_cstr(cstr,bstr)
- char *cstr;
- UBYTE bstr[];
- {
- UBYTE size,i;
- char *ptr;
-
- ptr = cstr;
- size = (bstr[0] <= BSTR_MAX) ? (bstr[0]) : (BSTR_MAX);
- for (i=1;i <= size;i++) *ptr++ = bstr[i];
- *ptr = '\0';
- return cstr;
- }
-
- /* This function converts the given directory pointer into a string
- path name, returning that path name. The "str" pointer is assumed
- to point to a long enough string to hold the entire path name. */
-
- char *strpath(str,dir)
- char *str;
- struct d_tree *dir;
- {
- if (dir == NULL) return (char *) strcpy(str,"");
- if (dir == root) return (char *) strcpy(str,":");
- if (dir->d_parent != NULL) strpath(str,dir->d_parent);
- if (strlen(str) != 1) strcat(str,"/");
- return (char *) strcat(str,dir->d_name);
- }
-
- /* This function creates an extended IO request port. */
-
- struct IORequest *CreateExtIO(ioReplyPort,size)
- struct MsgPort *ioReplyPort;
- LONG size;
- {
- struct IORequest *ioReq;
-
- if (ioReplyPort == NULL) return ((struct IORequest *) NULL);
- ioReq = (struct IORequest *) AllocMem(size, MEMF_CLEAR | MEMF_PUBLIC);
- if (ioReq == NULL) return ((struct IORequest *) NULL);
-
- ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
- ioReq->io_Message.mn_Node.ln_Pri = 0;
- ioReq->io_Message.mn_ReplyPort = ioReplyPort;
- return (ioReq);
- }
-
- /* This function deletes an extended IO request port. */
-
- DeleteExtIO(ioExt,size)
- struct IORequest *ioExt;
- LONG size;
- {
- ioExt->io_Message.mn_Node.ln_Type = 0xff;
- ioExt->io_Device = (struct Device *) -1;
- ioExt->io_Unit = (struct Unit *) -1;
- FreeMem(ioExt,size);
- }
-
- /* ====================================================================== */
-
- /* This section contains basic disk control functions. */
-
- /* This function turns off the motor of the selected disk drive. */
-
- MotorOff()
- {
- diskreq->iotd_Req.io_Length = 0;
- diskreq->iotd_Req.io_Command = TD_MOTOR;
- DoIO(diskreq);
- return(0);
- }
-
- /* This function initializes the selected disk device. */
-
- InitDisk()
- {
- diskreq->iotd_Req.io_Command = TD_CHANGENUM;
- DoIO(diskreq);
- diskChangeCount = diskreq->iotd_Req.io_Actual;
- }
-
- /* This function fills the buffer "buf" with the contents of the given
- sector number (0-17xx). Returns 0 if there's no error. It uses the
- bit map functions to set bit flags if the sector contains a hard
- error. This cuts down greatly on time, due to the fact that blocks
- with hard errors take longer to read (due to retrys). */
-
- BYTE ReadBuf(buf,sect)
- union block *buf;
- ULONG sect;
- {
- if (sect == 0) return 1;
- if (checkmap(badmap,sect)) return 1;
- diskreq->iotd_Req.io_Length = BLOCKSIZE;
- diskreq->iotd_Req.io_Data = (APTR) buf;
- diskreq->iotd_Req.io_Command = ETD_READ;
- diskreq->iotd_Count = diskChangeCount;
- diskreq->iotd_Req.io_Offset = TD_SECTOR * sect;
- DoIO(diskreq);
- if (diskreq->iotd_Req.io_Error != 0) {
- setmap(badmap,sect);
- setmap(usdmap,sect);
- }
- return (diskreq->iotd_Req.io_Error);
- }
-
- /* ====================================================================== */
-
- /* This section contains functions that operate on the directory tree
- and file list structures. */
-
- /* This function accepts a disk pointer for a file list link, which
- it will create and initialize. */
-
- struct f_list *Alloc_f_list(point)
- ULONG point;
- {
- struct f_list *temp;
-
- temp = (struct f_list *) AllocMem(sizeof(struct f_list),0);
- temp->f_file = point;
- temp->f_next = NULL;
- return temp;
- }
-
- /* This function will deallocate a complete file pointer list. */
-
- VOID Free_f_list(list)
- struct f_list *list;
- {
- if (list == NULL) return;
- if (list->f_next != NULL) Free_f_list(list->f_next);
- FreeMem(list,sizeof(struct f_list));
- }
-
- /* This function accepts a BSTR pointer, for which it will create a
- directory entry and initialize the name string from the BSTR. */
-
- struct d_tree *Alloc_d_tree(point,bstr)
- ULONG point;
- UBYTE bstr[];
- {
- struct d_tree *newtree;
-
- newtree = (struct d_tree *) AllocMem(sizeof(struct d_tree),0);
- newtree->d_flags = 0;
- newtree->d_next = NULL;
- newtree->d_parent = NULL;
- newtree->d_child = NULL;
- newtree->d_point = point;
- newtree->d_list = NULL;
- bstr_to_cstr(newtree->d_name,bstr);
- return newtree;
- }
-
- /* This function deallocates an entire d_tree. */
-
- Free_d_tree(tree)
- struct d_tree *tree;
- {
- if (tree->d_next != NULL) Free_d_tree(tree->d_next);
- if (tree->d_child != NULL) Free_d_tree(tree->d_child);
- if (tree->d_list != NULL) Free_f_list(tree->d_list);
- FreeMem(tree, sizeof(struct d_tree));
- }
-
- /* This function returns either the tree containing 'point' within 'tree',
- or NULL if 'point' does not yet exist. */
-
- struct d_tree *Find_d_tree(tree,point)
- struct d_tree *tree;
- ULONG point;
- {
- struct d_tree *temp;
-
- if (tree->d_point == point) return tree;
- if (tree->d_next != NULL)
- if ((temp = Find_d_tree(tree->d_next,point)) != NULL) return temp;
- if (tree->d_child != NULL)
- if ((temp = Find_d_tree(tree->d_child,point)) != NULL) return temp;
- return NULL;
- }
-
- /* This function adds the given tree to the child list of the given
- parent tree. */
-
- Add_Next_d_tree(parent, child)
- struct d_tree *parent, *child;
- {
- struct d_tree *temp;
-
- temp = parent->d_child;
- parent->d_child = child;
- child->d_next = temp;
- child->d_parent = parent;
- }
-
- /* This function will seek out a file, returning a pointer to its link
- structure if available, NULL otherwise. It looks for the file in the
- given directory structure, and only at that level. */
-
- struct f_list *Find_f_list(tree,point)
- struct d_tree *tree;
- ULONG point;
- {
- struct f_list *pnt;
-
- for(pnt = tree->d_list; pnt != NULL; pnt = pnt->f_next)
- if (pnt->f_file == point) break;
- return pnt;
- }
-
- /* This function links the file list "list" into the given tree's file
- list. */
-
- Add_Next_f_list(tree,list)
- struct d_tree *tree;
- struct f_list *list;
- {
- struct f_list *temp;
-
- temp = tree->d_list;
- tree->d_list = list;
- list->f_next = temp;
- }
-
- /* ====================================================================== */
-
- /* This function accepts a disk pointer, which is assumed to be the disk
- address of a directory structure that doesn't yet exist. This function
- will return a pointer to that directory after finding it on disk and
- linking it into the directory tree. If the directory can't be
- found on disk it will return the root directory. The effect of this
- function is a recursive lookahead up the path of the requested
- directory, which is the initial scanning process that moves the disk head
- in a non-linear fashion. */
-
- struct d_tree *MakeParent(dp)
- ULONG dp;
- {
- union block *tbuf; /* Temporary Disk Buffer */
- struct d_tree *tree; /* Requested directory */
- struct d_tree *dad; /* Its parent directory */
-
- tbuf = (union block *) AllocMem(BLOCKSIZE,MEMF_CHIP);
- if (ReadBuf(tbuf,dp) != READOK) {
- FreeMem(tbuf,BLOCKSIZE);
- return root;
- }
- if (tbuf->Gen.blk_type != FT_SHORT_FILE
- || tbuf->Gen.blk_subtype != ST_USER_DIR) {
- FreeMem(tbuf,BLOCKSIZE);
- return root;
- }
- if (tbuf->Dir.blk_name[0] == 0 || tbuf->Dir.blk_name[1] == 0) {
- FreeMem(tbuf,BLOCKSIZE);
- return root;
- }
- if ((dad = Find_d_tree(root,tbuf->Dir.blk_parent)) == NULL)
- dad = MakeParent(tbuf->Dir.blk_parent);
- tree = Alloc_d_tree(dp,tbuf->Dir.blk_name);
- Add_Next_d_tree(dad,tree);
- FreeMem(tbuf,BLOCKSIZE);
- return tree;
- }
-
- /* This function builds the directory list. The top node is kind of a
- default place, where any file who's directory is corrupted, as well
- as any files directly in the ROOT node, will be placed. This routine
- uses the global disk buffer "buf", so exercise caution when making
- any modifications not to recursively overwrite this buffer. This
- routine will tag any non-data block as "used" in the "usdmap" bitmap.
- Only actual data blocks remain untagged beyond this routine. */
-
- BuildDirTree()
- {
- ULONG sect; /* Current block */
- struct d_tree *dir, *sub; /* Working directory trees */
- struct f_list *fil; /* Working file list */
- char str[PATH_MAX]; /* Temporary string */
-
- for (sect = 1; sect < DISK_SIZE; sect++) {
- if (ReadBuf(buf,sect) != READOK) {
- printf(" \233\113\2337mBlock %4d (ERROR)\2330m\n",sect);
- continue;
- }
- switch (buf->Gen.blk_type) { /* Primary Type */
- case FT_SHORT_FILE :
- setmap(usdmap,sect);
- switch (buf->Gen.blk_subtype) { /* Secondary Type */
- case ST_FILE :
- printf(" \233\113Block %4d (FILE) Name \"",sect);
- if ((dir = Find_d_tree(root,buf->Fil.blk_parent)) == NULL)
- dir = MakeParent(buf->Fil.blk_parent);
- printf("%s",strpath(str,dir));
- if (str[strlen(str)-1] != ':') printf("/");
- printf("%s",bstr_to_cstr(str,buf->Fil.blk_name));
- if (sect != buf->Fil.blk_key) printf("\" Block Key Mismatch\n");
- else printf("\"\n");
- if (Find_f_list(dir,sect) != NULL) break;
- fil = Alloc_f_list(sect);
- Add_Next_f_list(dir,fil);
- break;
- case ST_USER_DIR :
- printf(" \233\113Block %4d (DIR) Name \"",sect);
- if ((dir = Find_d_tree(root,buf->Dir.blk_parent)) == NULL)
- dir = MakeParent(buf->Dir.blk_parent);
- if ((sub = Find_d_tree(dir,sect)) != NULL) {
- printf("%s\n",strpath(str,sub)); break;
- }
- sub = Alloc_d_tree(sect,buf->Dir.blk_name);
- Add_Next_d_tree(dir,sub);
- printf("%s",strpath(str,sub));
- if (sect != buf->Dir.blk_key) printf("\" Block Key Mismatch\n");
- else printf("\"\n");
- break;
- default :
- printf(" \233\113Block %4d (???)\n\233\101",sect);
- }
- break;
- case FT_DATA_BLOCK :
- if (buf->Dat.blk_size == 0) setmap(usdmap,sect);
- printf(" \233\113Block %4d (DATA)\n\233\101",sect); break;
- default :
- setmap(usdmap,sect);
- printf(" \233\113Block %4d (UNUSED)\n\233\101",sect); break;
- }
- }
- }
-
- /* ====================================================================== */
-
- /* This section contains routines concerned with actually restoring the
- file structure from the internal directory path structure. */
-
- /* This function resolves the order of likelihood of the two normal
- block links, returning as functional value the best guess. It will
- each check block on disk, and rule it out if either it specifies a
- disk location out of range or if its place on the disk is completely
- destroyed. There are two guesses for any unresolved block address, one
- for the data block link pointer, one for the file block header key
- pointer. Its only necessary to call this routine if the two values are
- different, i.e., at least one of the pointers has been messed up. This
- now also checks the "used" bit-map to avoid repeated/circular files being
- created. */
-
- ULONG FindPoints(g1,g2,seq,head)
- ULONG g1,g2,seq,head;
- {
- struct data_block *blk;
- UBYTE q1=0,q2=0;
-
- printf(" \2337mResolving link conflict %d <-> %d\2330m\n",g1,g2);
- if (g1 > DISK_SIZE) g1 = 0; if (g2 > DISK_SIZE) g2 = 0;
- if (checkmap(usdmap,g1)) g1=0;
- if (checkmap(usdmap,g2)) g2=0;
- if (g1 == 0) return g2; if (g2 == 0) return g1;
- blk = (struct data_block *) AllocMem(BLOCKSIZE,MEMF_CHIP);
- if (ReadBuf(blk,g1) == READOK)
- q1 = TRUE + (blk->blk_seq == seq) + (blk->blk_key == head) +
- (blk->blk_type == FT_DATA_BLOCK);
- if (ReadBuf(blk,g2) == READOK)
- q2 = TRUE + (blk->blk_seq == seq) + (blk->blk_key == head) +
- (blk->blk_type == FT_DATA_BLOCK);
- FreeMem(blk,BLOCKSIZE);
- if (q1 == 0) g1 = 0; if (q2 == 0) g2 = 0;
- if (q1 > q2) return g1; else return g2;
- }
-
- /* This function returns the next available file header data link
- pointer. If the current file header is exhausted it will open
- up an extension block based on the "eptr" disk pointer. */
-
- BOOL NewPoint(ptr,list,eptr)
- ULONG *ptr, **list, *eptr;
- {
- (*ptr)--;
- if (*ptr < 0) {
- if (ReadBuf(extn,*eptr) != READOK) {
- printf(" \2337mERROR: Bad Extension Block - No More Link Check\2330m\n");
- return FALSE;
- } else {
- printf(" Linking via Extension Block %d\n", extn->blk_key);
- *list = extn->blk_datalist;
- *eptr = extn->blk_extension;
- *ptr = LIST_SIZE-1;
- }
- }
- return TRUE;
- }
-
- /* This function rebuilds a single file from the passed file buffer
- information. It uses the list of data block keys to check the
- linking of the individual data blocks. It can resolve a corruption
- of either a data block chain pointer or of the data block key list.
- If an extension block is required its called for here; though if the
- extension block pointer has been corrupted then the rest of the
- file rebuild will have to take palace without verification from a
- data block key list. This function isn't currently recursive, as
- disk buffers "data" and "ext" are global variables. Every valid
- data block found is marked in the "used" bitmap. The function
- return TRUE if the remake works, FALSE if the remake fails. */
-
- BOOL FileRemake(file,blk)
- struct FileHandle *file;
- struct file_block *blk;
- {
- ULONG kptr, *klst; /* Manages file block keys. */
- ULONG link; /* Chained link key */
- ULONG eptr; /* Next extension block key. */
- ULONG seq; /* Block sequence number */
- BOOL ver=TRUE; /* Recover verification enabled */
-
- kptr = LIST_SIZE - 1;
- klst = blk->blk_datalist;
- eptr = blk->blk_extension;
- link = blk->blk_firstdata;
- seq = 0;
- data->blk_seq = 0;
- while (link!=0 || seq<blk->blk_seq && data->blk_seq<=blk->blk_seq) {
- seq++;
- if (link != klst[kptr])
- link = FindPoints(link,klst[kptr],seq,blk->blk_key);
- if (link == 0) break;
- if (ver) ver = NewPoint(&kptr,&klst,&eptr);
- if (ReadBuf(data,link) == READOK) {
- setmap(usdmap,link);
- if (Write(file,data->blk_data,min(BLOCKSIZE-6*WORDSIZE,
- data->blk_size)) == -1) {
- if (IoErr() == ERROR_DISK_FULL) return FALSE;
- }
- link = data->blk_next;
- } else if (ver) {
- printf(" \2337mERROR: Disk Fault -- File may be incomplete\2330m\n");
- link = klst[kptr];
- } else {
- printf(" \2337mERROR: Double Disk Fault -- File truncated\n\2330m");
- break;
- }
- }
- return TRUE;
- }
-
- /* This function accepts a string name. It creates a unique directory
- under that name, if possible. If it fails after a reasonable number
- of attempts, it returns FALSE, otherwise TRUE. */
-
- BOOL MakeUniqueDir(name)
- char *name;
- {
- struct FileLock *lock;
- int len;
- char addon;
-
- if ((lock = (struct FileLock *) CreateDir(name)) != 0) {
- printf("\2333mBuilding Directory \"%s\"\2330m\n", name);
- UnLock(lock);
- return TRUE;
- }
- printf("\2337mERROR: Bad Directory Create \"%s\", retrying...\2330m\n",name);
-
- len = strlen(name);
- name[len++] = '-';
- name[len+1] = '\0';
-
- for (addon = '0'; addon <= '9'; addon++) {
- name[len] = addon;
- if ((lock = (struct FileLock *) CreateDir(name)) != 0) {
- printf("\2333mBuilding Directory \"%s\"\2330m\n", name);
- UnLock(lock);
- return TRUE;
- }
- }
- return FALSE;
- }
-
- /* This function is called when a disk is assumed to be full. It will
- prompt the user for a new disk, then create the current directory on
- the new disk and exit, where the attempt to make the file take place
- again. */
-
- VOID BackTraceDir(fil)
- char *fil;
- {
- char newdsk[PATH_MAX];
- int i = 0;
-
- while ((newdsk[i++] = *fil++) != ':'); newdsk[i] = '\0';
- printf("\2337m\nDISK FULL: Insert New Disk In %s, type RETURN\2330m", newdsk);
- getchar();
- while (*fil) {
- while ((newdsk[i++] = *fil++) != '/') if (!*fil) return;
- newdsk[i-1] = '\0';
- if (!MakeUniqueDir(newdsk)) return;
- newdsk[i-1] = '/';
- }
- }
-
- /* This function rebuilds a directory tree. It will rebuild the contents of
- directory "dir" (just the files), and any siblings that "dir" may have. It
- also check each sibling for any children, and will recursively run itself
- on these children to effectively rebuild the entire directory tree. This
- function uses the global disk buffer "buf", so be very careful when making
- any recursive modifications that this buffer dosen't get overwritten. */
-
- VOID TreeRemake(dev,dir)
- char *dev;
- struct d_tree *dir;
- {
- struct f_list *list;
- struct d_tree *sibling;
- char filespec[PATH_MAX];
- UBYTE dirlen;
- struct FileHandle *fh;
- struct file_block *blk;
- BOOL fails = FALSE;
-
- blk = (struct file_block *) buf;
- strcpy(filespec,dev);
- for (sibling = dir; sibling != NULL; sibling = sibling->d_next) {
- strpath(filespec+3,sibling);
- if (sibling != root) if (!MakeUniqueDir(filespec)) continue;
- dirlen = strlen(filespec);
- if (filespec[dirlen-1] != ':') filespec[dirlen++] = '/';
- for (list = sibling->d_list; list != NULL; list = list->f_next) {
- if (ReadBuf(blk,list->f_file) != READOK) continue;
- bstr_to_cstr(filespec+dirlen,blk->blk_name);
- do {
- fails = FALSE;
- if ((fh = (struct FileHandle *) Open(filespec,MODE_NEWFILE)) == 0)
- printf(" \2337mERROR: Can't Create File \"%s\"\2330m\n", filespec);
- else {
- printf(" Recovering File \"%s\"\n", filespec);
- if (blk->blk_seq!=0 || blk->blk_firstdata!=0) {
- if (!FileRemake(fh,blk)) {
- Close(fh);
- DeleteFile(filespec);
- BackTraceDir(filespec);
- fails = TRUE;
- } else
- Close(fh);
- }
- }
- } while (fails);
- }
- if (sibling->d_child != NULL) TreeRemake(dev,sibling->d_child);
- }
- }
-
- /* ====================================================================== */
-
- /* This function salvages any loose blocks found floating around on the
- disk. They must be data blocks, and they must not be empty. The names
- of the blocks as saved are "BLK-####-####-##", where the first field
- is the block number found, the second field the indicated parent, and
- the third field the file's sequence number. They're stored in a
- directory named SPARE-BLOCKS##. The ## field is ususally 00, but
- if a SPARE-BLOCKS file already exists, this will be incremented to keep
- the directories unique. */
-
- VOID LooseSalv(dev)
- char *dev;
- {
- int i;
- BOOL clear = FALSE;
- char filespec[PATH_MAX];
- int dirext = -1;
- struct FileLock *lock;
- struct FileHandle *fh;
-
- for (i=0; i < DISK_SIZE/8 && !clear; i++) clear = (usdmap[i] == 0);
- if (clear) {
- printf(" No Loose Blocks Encountered\n");
- return;
- }
- while (TRUE) {
- sprintf(filespec,"%sSPARE-BLOCKS%d",dev,++dirext);
- if ((lock = (struct FileLock *) Lock(filespec,ACCESS_WRITE)) == 0)
- break;
- else
- UnLock(lock);
- }
- lock = (struct FileLock *) CreateDir(filespec); UnLock(lock);
- printf("\2333mCreated Directory %s\2330m\n",filespec);
- for (i=0; i < DISK_SIZE; i++)
- if (!checkmap(usdmap,i)) {
- ReadBuf(buf,i);
- sprintf(filespec,"%sSPARE-BLOCKS%d/BLK-%d-%d-%d",
- dev,dirext,i,buf->Dat.blk_key,buf->Dat.blk_seq);
- printf(" Saving %s\n",filespec);
- fh = (struct FileHandle *) Open(filespec,MODE_NEWFILE);
- Write(fh,buf->Dat.blk_data,
- min(BLOCKSIZE-6*WORDSIZE,buf->Dat.blk_size));
- Close(fh);
- }
- }
-
- /* ====================================================================== */
-
- /* This function attempts to open the proper I/O devices and memory space,
- then perform a disk salvage. If at any point the disk salvage cannot
- be properly done, the function returns FALSE. If successful, it will
- return TRUE. Strings "old" and "new" are the names of the source and
- destination drives. */
-
- BOOL do_it(old,new)
- char *old, *new;
- {
- char dummy;
-
- /* This is the stuff that need opening... */
- if ((diskport = (struct MsgPort *) CreatePort("diskreq.port",0)) == NULL) {
- printf("\2337mERROR: Cannot create message port\2330m\n");
- return FALSE;
- }
- if ((diskreq = (struct IOExtTD *)
- CreateExtIO(diskport,sizeof(struct IOExtTD))) == NULL) {
- printf("\2337mERROR: Cannot create I/O port\2330m\n");
- return FALSE;
- }
- if ((buf = (union block *)AllocMem(BLOCKSIZE,MEMF_CHIP))==NULL)
- return FALSE;
- if ((data = (struct data_block *)AllocMem(BLOCKSIZE,MEMF_CHIP))==NULL)
- return FALSE;
- if ((extn = (struct extens_block *)AllocMem(BLOCKSIZE,MEMF_CHIP))==NULL)
- return FALSE;
- if (OpenDevice(TD_NAME,device,diskreq,0)) {
- printf("\2337mERROR: Cannot open TrackDisk device\2330m\n");
- return FALSE;
- }
- /* Looks like we made it. Let's boogie. */
- printf("DiskSalv %s by Dave Haynie\n\n",prog_version);
- printf("Place disk to salvage FROM in drive %s\n", old);
- printf("Place disk to salvage TO in drive %s\n", new);
- printf("Type RETURN to continue");
- dummy = getchar();
- InitDisk();
- /* Let's clear the bitmaps */
- zeromap(badmap);
- zeromap(usdmap);
- setmap(usdmap,0);
- /* Its simple to build the directory tree from here. */
- printf("\nBuilding Directory Map...\n");
- root = Alloc_d_tree(880,"\001:0");
- BuildDirTree();
- /* And just as simple to copy all that stuff over to the new disk. */
- printf("\nSalvaging Disk Structure...\n");
- TreeRemake(new,root);
- /* Now save any stray blocks. */
- printf("\nSalvaging Loose Blocks...\n");
- LooseSalv(new);
- /* Finally a preliminary clean-up. */
- printf("Cleaning Up...\n");
- Free_d_tree(root);
- return TRUE;
- }
-
- /* This is the main program. It checks to make sure that you type things
- the way I like them, then it calls the do_it routine to really do stuff. */
-
- main(argc,argv)
- int argc;
- char *argv[];
- {
- char *in_dev, *out_dev, temp, stat;
-
- switch (argc) {
- case 2:
- if (argv[1][0] == '?') {
- printf("DiskSalv %s (c)1986 by \"Hazy\" Dave Haynie\n\n",
- prog_version);
- printf(" This program salvages a bad AmigaDOS disk by copying any\n");
- printf("recoverable files or directories from the bad disk to a good\n");
- printf("formatted disk. It also copies loose data blocks (blocks\n");
- printf("who's file header was destroyed). The format is:\n\n");
- printf(" DiskSalv dfM: [TO] dfN:\n\n");
- printf("where N and M are different 3 1/2\" drives from 0 to 3.\n");
- printf(" This program may be distributed free for any personal\n");
- printf("usage without additional permission. If you find this\n");
- printf("program very useful, please consider paying $10 or less for\n");
- printf("its use. You may also direct any questions, bug reports,\n");
- printf("or macadamia nuts to the author at:\n");
- printf(" Dave Haynie\n 645 Allen Avenue\n Gibbstown, NJ 08027\n");
- printf(" Usenet : {allegra,caip,seismo}!cbmvax!daveh\n");
- printf(" Compuserve: 76703,2047\n QuantumLink: Animal128\n");
- exit();
- }
- default:
- printf("Usage: DiskSalv dfM: [TO] dfN:, type \"DiskSalv ?\" for help.\n");
- exit();
- case 3:
- in_dev = argv[1]; out_dev = argv[2]; break;
- case 4:
- in_dev = argv[1]; out_dev = argv[3]; break;
- }
- device = in_dev[2] - '0';
- if (device < 0 || device > 4) {
- printf("\2337mERROR: Invalid input drive specification\2330m\n");
- exit();
- }
- temp = out_dev[2] - '0';
- if (temp < 0 || temp > 4) {
- printf("\2337mERROR: Invalid output drive specification\2330m\n");
- exit();
- }
- if (temp == device) {
- printf("\2337mSORRY: Only 2-drive salvage is currently supported\2330m\n");
- exit();
- }
- stat = do_it(in_dev,out_dev); /* Perform the recover function. */
- if (data != NULL) FreeMem(data,BLOCKSIZE);
- if (extn != NULL) FreeMem(extn,BLOCKSIZE);
- if (buf != NULL) FreeMem(buf,BLOCKSIZE);
- if (diskreq != NULL) {
- MotorOff();
- CloseDevice(diskreq);
- DeleteExtIO(diskreq, sizeof(struct IOExtTD));
- }
- if (diskport != NULL) DeletePort(diskport);
- exit(stat);
- }
-
-
-